package com.gitee.linmt.register;

import com.gitee.linmt.base.BaseDictionaryEnum;
import com.gitee.linmt.config.DictionaryEnumAutoConfig;
import com.gitee.linmt.exception.DictionaryCodeCheckException;
import com.gitee.linmt.handle.DictionaryEnumHandler;
import com.gitee.linmt.handle.DictionaryEnumsHandler;
import com.gitee.linmt.properties.DictionaryEnumProperties;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.logging.log4j.util.Strings;
import org.mybatis.logging.Logger;
import org.mybatis.logging.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.lang.NonNull;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @author lin-mt
 * @version 1.0.0
 * @since 1.0.0
 */
public class DictionaryEnumTypeHandlerRegister<E extends BaseDictionaryEnum> implements ApplicationListener<ContextRefreshedEvent> {

    private static final Logger LOGGER = LoggerFactory.getLogger(DictionaryEnumTypeHandlerRegister.class);
    private final Set<Class<E>> dictionaryEnums;
    private final DictionaryEnumProperties dictionaryEnumProperties;

    public DictionaryEnumTypeHandlerRegister(Set<Class<E>> dictionaryEnums, @NonNull DictionaryEnumProperties dictionaryEnumProperties) {
        this.dictionaryEnums = dictionaryEnums;
        this.dictionaryEnumProperties = dictionaryEnumProperties;
        if (dictionaryEnumProperties.getEnableCodeCheck()) {
            // 检查Code
            checkDictionaryCode();
        }
    }

    private void checkDictionaryCode() {
        Map<String, Class<E>> allCode = new HashMap<>();
        for (Class<E> dictionaryEnum : this.dictionaryEnums) {
            if (dictionaryEnum.isEnum()) {
                E[] enumConstants = dictionaryEnum.getEnumConstants();
                for (E enumConstant : enumConstants) {
                    // 检查Code长度是否大于一层的长度
                    if (Strings.isBlank(enumConstant.getCode()) ||
                            enumConstant.getCode().length() < dictionaryEnumProperties.getOneLevelLength()) {
                        throw new DictionaryCodeCheckException(
                                String.format("%s(%s)：Code长度必须大于 %s", dictionaryEnum, enumConstant.getCode(),
                                        dictionaryEnumProperties.getOneLevelLength()));
                    }
                    // 检查Code的层数是否为一层长度的整数倍
                    if (enumConstant.getCode().length() % dictionaryEnumProperties.getOneLevelLength() != 0) {
                        throw new DictionaryCodeCheckException(
                                String.format("%s(%s)：Code长度必须是 %s 的整数倍", dictionaryEnum, enumConstant.getCode(),
                                        dictionaryEnumProperties.getOneLevelLength()));
                    }
                    // 检查Code层级是否大于最大值
                    if (enumConstant.getCode().length() / dictionaryEnumProperties.getOneLevelLength() >
                            dictionaryEnumProperties.getMaxLevelCount()) {
                        throw new DictionaryCodeCheckException(String.format("%s(%s)：Code层级不能大于 %s",
                                dictionaryEnum, enumConstant.getCode(),
                                dictionaryEnumProperties.getMaxLevelCount()));
                    }
                    // 检查是否存在重复的Code
                    if (allCode.containsKey(enumConstant.getCode())) {
                        String duplicatedCodeMessage = String.format("%s 与 %s 出现了重复的Code：%s, ",
                                allCode.get(enumConstant.getCode()), dictionaryEnum,
                                enumConstant.getCode());
                        switch (this.dictionaryEnumProperties.getDuplicated()){
                            case EXCEPTION:
                                throw new DictionaryCodeCheckException(duplicatedCodeMessage);
                            case ERROR:
                                LOGGER.error(() -> duplicatedCodeMessage);
                                break;
                            case WARNING:
                                LOGGER.warn(() -> duplicatedCodeMessage);
                                break;
                            default:
                                // 默认值是抛出异常，不会为null，所以这里不处理
                                break;
                        }
                    } else {
                        allCode.put(enumConstant.getCode(), dictionaryEnum);
                    }
                }
            }
        }
    }

    @Override
    public void onApplicationEvent(@NonNull ContextRefreshedEvent applicationEvent) {
        if (!CollectionUtils.isEmpty(dictionaryEnums)) {
            ApplicationContext applicationContext = applicationEvent.getApplicationContext();
            SqlSessionFactory sqlSessionFactory = applicationContext.getBean(SqlSessionFactory.class);
            try {
                TypeHandlerRegistry typeHandlerRegistry = sqlSessionFactory.getConfiguration().getTypeHandlerRegistry();
                Set<Class<E>> dictionaryEnumInterfaces = new HashSet<>();
                Iterator<Class<E>> iterator = dictionaryEnums.iterator();
                while (iterator.hasNext()) {
                    Class<E> next = iterator.next();
                    if (next.isEnum()) {
                        continue;
                    }
                    if (next.isInterface()) {
                        dictionaryEnumInterfaces.add(next);
                    }
                    // 去掉非枚举的类
                    iterator.remove();
                }
                for (Class<E> dictionaryEnumClass : dictionaryEnums) {
                    typeHandlerRegistry.register(dictionaryEnumClass, new DictionaryEnumHandler<>(dictionaryEnumClass));
                }
                if (!CollectionUtils.isEmpty(dictionaryEnumInterfaces)) {
                    Set<TypeHandler<E>> typeHandlers = new HashSet<>();
                    Set<Class<E>> subclasses = new HashSet<>();
                    for (Class<E> dictionaryEnumInterface : dictionaryEnumInterfaces) {
                        for (Class<E> dictionaryEnum : dictionaryEnums) {
                            if (dictionaryEnumInterface.isAssignableFrom(dictionaryEnum)) {
                                subclasses.add(dictionaryEnum);
                            }
                        }
                        if (!CollectionUtils.isEmpty(subclasses)) {
                            for (Class<E> subclass : subclasses) {
                                typeHandlers.add(typeHandlerRegistry.getTypeHandler(subclass));
                            }
                            typeHandlerRegistry.register(dictionaryEnumInterface, new DictionaryEnumsHandler<>(typeHandlers));
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public Set<Class<E>> getDictionaryEnums() {
        return dictionaryEnums;
    }
}
